home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2009 May / maximum-cd-2009-05.iso / DiscContents / Firefox Setup 3.0.6.exe / nonlocalized / components / nsTaggingService.js < prev    next >
Encoding:
Text File  |  2009-01-19  |  9.7 KB  |  311 lines

  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  * ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License
  6.  * Version 1.1 (the "License"); you may not use this file except in
  7.  * compliance with the License. You may obtain a copy of the License
  8.  * at http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS"
  11.  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  12.  * the License for the specific language governing rights and
  13.  * limitations under the License.
  14.  *
  15.  * The Original Code is the Places Tagging Service.
  16.  *
  17.  * The Initial Developer of the Original Code is
  18.  * Mozilla Corporation.
  19.  * Portions created by the Initial Developer are Copyright (C) 2007
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *   Asaf Romano <mano@mozilla.com> (Original Author)
  24.  *
  25.  * Alternatively, the contents of this file may be used under the terms of
  26.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  27.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28.  * in which case the provisions of the GPL or the LGPL are applicable instead
  29.  * of those above. If you wish to allow use of your version of this file only
  30.  * under the terms of either the GPL or the LGPL, and not to allow others to
  31.  * use your version of this file under the terms of the MPL, indicate your
  32.  * decision by deleting the provisions above and replace them with the notice
  33.  * and other provisions required by the GPL or the LGPL. If you do not delete
  34.  * the provisions above, a recipient may use your version of this file under
  35.  * the terms of any one of the MPL, the GPL or the LGPL.
  36.  *
  37.  * ***** END LICENSE BLOCK ***** */
  38.  
  39. const Cc = Components.classes;
  40. const Ci = Components.interfaces;
  41. const Cr = Components.results;
  42.  
  43. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  44.  
  45. const NH_CONTRACTID = "@mozilla.org/browser/nav-history-service;1";
  46. const BMS_CONTRACTID = "@mozilla.org/browser/nav-bookmarks-service;1";
  47. const IO_CONTRACTID = "@mozilla.org/network/io-service;1";
  48. const ANNO_CONTRACTID = "@mozilla.org/browser/annotation-service;1";
  49. const FAV_CONTRACTID = "@mozilla.org/browser/favicon-service;1";
  50. const OBSS_CONTRACTID = "@mozilla.org/observer-service;1";
  51.  
  52. var gIoService = Cc[IO_CONTRACTID].getService(Ci.nsIIOService);
  53.  
  54. /**
  55.  * The Places Tagging Service
  56.  */
  57. function TaggingService() {
  58. }
  59.  
  60. TaggingService.prototype = {
  61.   get _bms() {
  62.     if (!this.__bms)
  63.       this.__bms = Cc[BMS_CONTRACTID].getService(Ci.nsINavBookmarksService);
  64.     return this.__bms;
  65.   },
  66.  
  67.   get _history() {
  68.     if (!this.__history)
  69.       this.__history = Cc[NH_CONTRACTID].getService(Ci.nsINavHistoryService);
  70.     return this.__history;
  71.   },
  72.  
  73.   get _annos() {
  74.     if (!this.__annos)
  75.       this.__annos =  Cc[ANNO_CONTRACTID].getService(Ci.nsIAnnotationService);
  76.     return this.__annos;
  77.   },
  78.  
  79.   get _tagsResult() {
  80.     if (!this.__tagsResult) {
  81.       var options = this._history.getNewQueryOptions();
  82.       var query = this._history.getNewQuery();
  83.       query.setFolders([this._bms.tagsFolder], 1);
  84.       this.__tagsResult = this._history.executeQuery(query, options);
  85.       this.__tagsResult.root.containerOpen = true;
  86.  
  87.       // we need to null out the result on shutdown
  88.       var observerSvc = Cc[OBSS_CONTRACTID].getService(Ci.nsIObserverService);
  89.       observerSvc.addObserver(this, "xpcom-shutdown", false);
  90.     }
  91.     return this.__tagsResult;
  92.   },
  93.  
  94.   // Feed XPCOMUtils
  95.   classDescription: "Places Tagging Service",
  96.   contractID: "@mozilla.org/browser/tagging-service;1",
  97.   classID: Components.ID("{bbc23860-2553-479d-8b78-94d9038334f7}"),
  98.  
  99.   // nsISupports
  100.   QueryInterface: XPCOMUtils.generateQI([Ci.nsITaggingService,
  101.                                          Ci.nsIObserver]),
  102.  
  103.   /**
  104.    * If there's no tag with the given name, null is returned;
  105.    */
  106.   _getTagNode: function TS__getTagIndex(aTagNameOrId) {
  107.     if (!aTagNameOrId)
  108.       throw Cr.NS_ERROR_INVALID_ARG;
  109.  
  110.     var nameLower = null;
  111.     if (typeof(aTagNameOrId) == "string")
  112.       nameLower = aTagNameOrId.toLowerCase();
  113.  
  114.     var root = this._tagsResult.root;
  115.     var cc = root.childCount;
  116.     for (var i=0; i < cc; i++) {
  117.       var child = root.getChild(i);
  118.       if ((nameLower && child.title.toLowerCase() == nameLower) ||
  119.           child.itemId === aTagNameOrId)
  120.         return child;
  121.     }
  122.  
  123.     return null;
  124.   },
  125.  
  126.   /**
  127.    * Creates a tag container under the tags-root with the given name.
  128.    *
  129.    * @param aName
  130.    *        the name for the new container.
  131.    * @returns the id of the new container.
  132.    */
  133.   _createTag: function TS__createTag(aName) {
  134.     return this._bms.createFolder(this._bms.tagsFolder, aName,
  135.                                   this._bms.DEFAULT_INDEX);
  136.   },
  137.  
  138.   /**
  139.    * Checks whether the given uri is tagged with the given tag.
  140.    *
  141.    * @param [in] aURI
  142.    *        url to check for
  143.    * @param [in] aTagId
  144.    *        id of the folder representing the tag to check
  145.    * @param [out] aItemId
  146.    *        the id of the item found under the tag container
  147.    * @returns true if the given uri is tagged with the given tag, false
  148.    *          otherwise.
  149.    */
  150.   _isURITaggedInternal: function TS__uriTagged(aURI, aTagId, aItemId) {
  151.     var bookmarkIds = this._bms.getBookmarkIdsForURI(aURI, {});
  152.     for (var i=0; i < bookmarkIds.length; i++) {
  153.       var parent = this._bms.getFolderIdForItem(bookmarkIds[i]);
  154.       if (parent == aTagId) {
  155.         aItemId.value = bookmarkIds[i];
  156.         return true;
  157.       }
  158.     }
  159.     return false;
  160.   },
  161.  
  162.   // nsITaggingService
  163.   tagURI: function TS_tagURI(aURI, aTags) {
  164.     if (!aURI || !aTags)
  165.       throw Cr.NS_ERROR_INVALID_ARG;
  166.  
  167.     for (var i=0; i < aTags.length; i++) {
  168.       var tagNode = this._getTagNode(aTags[i]);
  169.       if (!tagNode) {
  170.         if (typeof(aTags[i]) == "number")
  171.           throw Cr.NS_ERROR_INVALID_ARG;
  172.  
  173.         var tagId = this._createTag(aTags[i]);
  174.         this._bms.insertBookmark(tagId, aURI, this._bms.DEFAULT_INDEX, null);
  175.       }
  176.       else {
  177.         var tagId = tagNode.itemId;
  178.         if (!this._isURITaggedInternal(aURI, tagNode.itemId, {}))
  179.           this._bms.insertBookmark(tagId, aURI, this._bms.DEFAULT_INDEX, null);
  180.  
  181.         // _getTagNode ignores case sensitivity
  182.         // rename the tag container so the places view would match the
  183.         // user-typed values
  184.         if (typeof(aTags[i]) == "string" && tagNode.title != aTags[i])
  185.           this._bms.setItemTitle(tagNode.itemId, aTags[i]);
  186.       }
  187.     }
  188.   },
  189.  
  190.   /**
  191.    * Removes the tag container from the tags-root if the given tag is empty.
  192.    *
  193.    * @param aTagId
  194.    *        the item-id of the tag element under the tags root
  195.    */
  196.   _removeTagIfEmpty: function TS__removeTagIfEmpty(aTagId) {
  197.     var node = this._getTagNode(aTagId).QueryInterface(Ci.nsINavHistoryContainerResultNode);
  198.     var wasOpen = node.containerOpen;
  199.     if (!wasOpen)
  200.       node.containerOpen = true;
  201.     var cc = node.childCount;
  202.     if (wasOpen)
  203.       node.containerOpen = false;
  204.     if (cc == 0) {
  205.       this._bms.removeFolder(node.itemId);
  206.     }
  207.   },
  208.  
  209.   // nsITaggingService
  210.   untagURI: function TS_untagURI(aURI, aTags) {
  211.     if (!aURI)
  212.       throw Cr.NS_ERROR_INVALID_ARG;
  213.  
  214.     if (!aTags) {
  215.       // see IDL.
  216.       // XXXmano: write a perf-sensitive version of this code path...
  217.       aTags = this.getTagsForURI(aURI, { });
  218.     }
  219.  
  220.     for (var i=0; i < aTags.length; i++) {
  221.       var tagNode = this._getTagNode(aTags[i]);
  222.       if (tagNode) {
  223.         var itemId = { };
  224.         if (this._isURITaggedInternal(aURI, tagNode.itemId, itemId)) {
  225.           this._bms.removeItem(itemId.value);
  226.           this._removeTagIfEmpty(tagNode.itemId);
  227.         }
  228.       }
  229.       else if (typeof(aTags[i]) == "number")
  230.         throw Cr.NS_ERROR_INVALID_ARG;
  231.     }
  232.   },
  233.  
  234.   // nsITaggingService
  235.   getURIsForTag: function TS_getURIsForTag(aTag) {
  236.     if (aTag.length == 0)
  237.       throw Cr.NS_ERROR_INVALID_ARG;
  238.  
  239.     var uris = [];
  240.     var tagNode = this._getTagNode(aTag);
  241.     if (tagNode) {
  242.       tagNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
  243.       tagNode.containerOpen = true;
  244.       var cc = tagNode.childCount;
  245.       for (var i = 0; i < cc; i++) {
  246.         try {
  247.           uris.push(gIoService.newURI(tagNode.getChild(i).uri, null, null));
  248.         } catch (ex) {
  249.           // This is an invalid node, tags should only contain valid uri nodes.
  250.           // continue to next node.
  251.         }
  252.       }
  253.       tagNode.containerOpen = false;
  254.     }
  255.     return uris;
  256.   },
  257.  
  258.   // nsITaggingService
  259.   getTagsForURI: function TS_getTagsForURI(aURI, aCount) {
  260.     if (!aURI)
  261.       throw Cr.NS_ERROR_INVALID_ARG;
  262.  
  263.     var tags = [];
  264.     var bookmarkIds = this._bms.getBookmarkIdsForURI(aURI, {});
  265.     var root = this._tagsResult.root;
  266.     var cc = root.childCount;
  267.     for (var i=0; i < bookmarkIds.length; i++) {
  268.       var parent = this._bms.getFolderIdForItem(bookmarkIds[i]);
  269.       for (var j=0; j < cc; j++) {
  270.         var child = root.getChild(j);
  271.         if (child.itemId == parent)
  272.           tags.push(child.title);
  273.       }
  274.     }
  275.  
  276.     // sort the tag list
  277.     tags.sort();
  278.     aCount.value = tags.length;
  279.     return tags;
  280.   },
  281.  
  282.   // nsITaggingService
  283.   get allTags() {
  284.     var tags = [];
  285.     var root = this._tagsResult.root;
  286.     var cc = root.childCount;
  287.     for (var j=0; j < cc; j++) {
  288.       var child = root.getChild(j);
  289.       tags.push(child.title);
  290.     }
  291.  
  292.     // sort the tag list
  293.     tags.sort();
  294.     return tags;
  295.   },
  296.  
  297.   // nsIObserver
  298.   observe: function TS_observe(subject, topic, data) {
  299.     if (topic == "xpcom-shutdown") {
  300.       this.__tagsResult.root.containerOpen = false;
  301.       this.__tagsResult = null;
  302.       var observerSvc = Cc[OBSS_CONTRACTID].getService(Ci.nsIObserverService);
  303.       observerSvc.removeObserver(this, "xpcom-shutdown");
  304.     }
  305.   }
  306. };
  307.  
  308. function NSGetModule(compMgr, fileSpec) {
  309.   return XPCOMUtils.generateModule([TaggingService]);
  310. }
  311.